package com.doyens.gmall.cart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.doyens.gmall.cart.mapper.CartInfoMapper;
import com.doyens.gmall.cart.service.CartAsyncService;
import com.doyens.gmall.cart.service.CartService;
import com.doyens.gmall.common.constant.RedisConst;
import com.doyens.gmall.common.util.DateUtil;
import com.doyens.gmall.model.cart.CartInfo;
import com.doyens.gmall.model.product.SkuInfo;
import com.doyens.gmall.product.client.ProductFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private CartAsyncService cartAsyncService;

    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        //查询这条购物车数据，从redis中查询，要求redis中的数据跟数据库保持一致

        //TODO:检查redis中的数据，如果没有，从数据库更新到redis中

        //创建redis的key
        String cartKey = getCartKey(userId);
        //获取redis中当前用户的所有购物车数据的map集合(skuId是key，cartInfo是value)
        BoundHashOperations cartRedisMap = redisTemplate.boundHashOps(cartKey);
        //通过skuId查询redis中的cartInfo
        CartInfo cartInfoExist = (CartInfo) cartRedisMap.get(skuId.toString());
        if (cartInfoExist == null) {
            //redis没有有这条购物车数据
            //通过skuId，去producet中查询skuInfo
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            //根据skuInfo，拼凑cartInfo
            cartInfoExist = new CartInfo();
            //添加到购物车的数据，默认选中
            cartInfoExist.setIsChecked(1);
            //实时价格
            cartInfoExist.setCartPrice(skuInfo.getPrice());
            cartInfoExist.setSkuPrice(skuInfo.getPrice());
            //设置添加到购物车的时间
            cartInfoExist.setCreateTime(new Timestamp(new Date().getTime()));//时间戳 年月日 时分秒
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            cartInfoExist.setUserId(userId);
            cartInfoExist.setSkuId(skuId);
            cartInfoExist.setSkuNum(skuNum);
            //商品名字
            cartInfoExist.setSkuName(skuInfo.getSkuName());
            cartInfoExist.setImgUrl(skuInfo.getSkuDefaultImg());
            //异步添加到数据库
            cartAsyncService.add(cartInfoExist);
        } else {
            //redis有这条购物车数据
            //修改数量(原来的数量+skuNum)
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum() + skuNum);
            BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
            //更新最新价格
            cartInfoExist.setSkuPrice(skuPrice);
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            //异步修改到数据库
            cartAsyncService.update(cartInfoExist);

        }
        //修改到redis
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(), cartInfoExist);
        //设置过期时间，7天
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    @Override
    /**
     * 查询购物车，根据用户id或者临时用户id查询
     */
    public List<CartInfo> getCartList(String userId, String userTempId) {
        //从redis中查询
        if (StringUtils.isEmpty(userId)) {
            //查询未登录的临时用户的购物车信息
            List<CartInfo> cartInfos = getCartList(userTempId);
            return cartInfos;
        } else {//用户登录
            List<CartInfo> cartInfos = null;
            //用户登录了，有用户ID，并且未登录的时候，也有临时用户id，需要合并购物车
            if (!StringUtils.isEmpty(userTempId)) {
                List<CartInfo> cartNoLoginList = getCartList(userTempId);
                if (!CollectionUtils.isEmpty(cartNoLoginList)) {
                    // 登录了，也有临时用户的购物车数据，必须合并购物车
                    cartInfos = mergeCartList(cartNoLoginList, userId);
                    //删除临时用户的购物车数据
                    deleteCarts(userTempId);
                    return cartInfos;
                }

            }
            //登录了，但是没有未登录数据，不需要合并购物车，只需要查用户id的购物车
            if (CollectionUtils.isEmpty(cartInfos)) {
                cartInfos = getCartList(userId);
            }
            return cartInfos;
        }
    }

    /**
     * 根据用户id/临时用户id删除购物车
     *
     * @param userId
     */
    private void deleteCarts(String userId) {
        QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",userId);
        cartInfoMapper.delete(queryWrapper);

        //删除redis缓存
        String cartKey = getCartKey(userId);
        if(redisTemplate.hasKey(cartKey)){
            redisTemplate.delete(cartKey);
        }
    }

    /**
     * 合并购物车
     *
     * @param cartNoLoginList
     * @param userId
     * @return
     */
    private List<CartInfo> mergeCartList(List<CartInfo> cartNoLoginList, String userId) {
        List<CartInfo> cartList = getCartList(userId);
        if (CollectionUtils.isEmpty(cartList)) {
            //临时用户有购物车数据，登录用户无购物车数据
            //只需要把临时用户的数据，添加到登录用户的id下
            cartNoLoginList.forEach(cartInfo -> {
                //设置登录用户的id
                cartInfo.setUserId(userId);
                //修改时间
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                //修改价格
                BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
                cartInfo.setSkuPrice(skuPrice);
                cartInfo.setId(null);
//                cartInfoMapper.insert(cartInfo);
                cartAsyncService.add(cartInfo);

            });

        } else {
            //41 1
            //42 1

            //42 23
            //43 1

            //41 1
            //42 2
            //43 1
            //把登录的购物车信息根据skuId转换成map
            Map<Long, CartInfo> cartInfoMap = cartList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));

            //循环未登录购物车数据
            cartNoLoginList.forEach(cartInfoNologin -> {
                //判断登录后的购物车包含未登录的购物车商品
                if (cartInfoMap.containsKey(cartInfoNologin.getSkuId())) {
                    //未登录的这个购物车数据，在登录的购物车里也有
                    //修改购物车数量 shitf+alt+r
                    CartInfo cartInfoLogin = cartInfoMap.get(cartInfoNologin.getSkuId());
                    //把登录后的购物车商品的数量修改成登录后的+登录前的数量
                    cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum() + cartInfoNologin.getSkuNum());
                    cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));
                    BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfoNologin.getSkuId());
                    cartInfoLogin.setSkuPrice(skuPrice);
                    //购物车的选中状态，已未登录的为准
                    if (cartInfoNologin.getIsChecked() == 1) {
                        cartInfoLogin.setIsChecked(1);
                    }
                    //修改到数据库
                    QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("user_id", userId);
                    queryWrapper.eq("sku_id", cartInfoNologin.getSkuId());
//                    cartInfoMapper.update(cartInfoLogin, queryWrapper);
                    cartAsyncService.update(cartInfoLogin);

                } else {
                    //未登录的购物车，在登录中没有，新增
                    cartInfoNologin.setUserId(userId);
                    cartInfoNologin.setUpdateTime(new Timestamp(new Date().getTime()));
                    BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfoNologin.getSkuId());
                    cartInfoNologin.setSkuPrice(skuPrice);
                    cartInfoNologin.setIsChecked(1);
//                    cartInfoMapper.insert(cartInfoNologin);
                    cartAsyncService.add(cartInfoNologin);
                }
            });
        }
        //不管怎么合并购物车，都需要重新查数据库，放入redis中
        List<CartInfo> cartInfos = loadCartCache(userId);
        return cartInfos;
    }

    /**
     * 根据用户id/用户临时id查询购物车
     *
     * @param userId
     * @return
     */
    private List<CartInfo> getCartList(String userId) {
        //查看redis中是否有数据
        String cartKey = getCartKey(userId);
        //redis中有这个key
        if (redisTemplate.hasKey(cartKey)) {
            BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
            //取redis中的数据
            List<CartInfo> cartInfos = boundHashOperations.values();
            if (!CollectionUtils.isEmpty(cartInfos)) {
                //购物车页面展示的数据，通过时间倒序排
                cartInfos.sort((o1, o2) -> {
                    return DateUtil.truncatedCompareTo(o2.getUpdateTime(), o1.getUpdateTime(), Calendar.SECOND);
                });
                //redis中查询到的数据，直接返回
                return cartInfos;
            }
            return new ArrayList<>();
        } else {
            //redis中没有这个key（1 没存到redis 2 数据库也没有）
            //查询数据库
            List<CartInfo> cartInfos = loadCartCache(userId);
            return cartInfos;
        }
    }

    /**
     * 根据用户id/临时用户id 从数据库查询购物车数据
     * 同步到缓存中
     *
     * @param userId
     * @return
     */
    public List<CartInfo> loadCartCache(String userId) {
        QueryWrapper<CartInfo> queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id", userId);
        List<CartInfo> cartInfos = cartInfoMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(cartInfos)) {
            //数据库也没数据
            return cartInfos;
        } else {
            //数据库有数据
            //存redis
            String cartKey = getCartKey(userId);
            Map<String, CartInfo> map = new HashMap<>();
            for (CartInfo cartInfo : cartInfos) {
                //重新查询价格
                BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
                cartInfo.setSkuPrice(skuPrice);
                map.put(cartInfo.getSkuId().toString(), cartInfo);
            }
            //存redis
            redisTemplate.boundHashOps(cartKey).putAll(map);
            //设置过期时间
            redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
            return cartInfos;
        }
    }

    private String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }

    @Override
    public void deleteCart(Long skuId, String userId) {
        //删除redis中的购物车
        String cartKey = getCartKey(userId);
        if(redisTemplate.hasKey(cartKey)){
            redisTemplate.boundHashOps(cartKey).delete(skuId.toString());
        }
        //删除数据中的
//        QueryWrapper<CartInfo> queryWrapper = new QueryWrapper();
//        queryWrapper.eq("sku_id",skuId);
//        queryWrapper.eq("user_id",userId);
//        cartInfoMapper.delete(queryWrapper);
        cartAsyncService.delete(skuId,userId);
    }

    @Override
    public void checkCart(Long skuId, Integer isChecked, String userId) {
        //修改redis中的状态
        String cartKey = getCartKey(userId);
        //判断redis中是否有这个用户的缓存
        if(redisTemplate.hasKey(cartKey)){
            //取这个用户的购物车缓存
            BoundHashOperations<String,String,CartInfo> boundHashOps = redisTemplate.boundHashOps(cartKey);
            //判断购物车缓存有没有这个商品信息
            if(boundHashOps.hasKey(skuId.toString())){
                //取购物车缓存中的这个商品
               CartInfo cartInfo = boundHashOps.get(skuId.toString());
               //修改选中状态
                cartInfo.setIsChecked(isChecked);
                //会自动修改redis中的数据
                boundHashOps.put(skuId.toString(),cartInfo);
                //重置有效期
                redisTemplate.expire(cartKey,RedisConst.USER_CART_EXPIRE,TimeUnit.SECONDS);
            }
        }
        //异步修改数据库中的状态
        cartAsyncService.checkCart(userId,skuId,isChecked);
    }

    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        String cartKey = getCartKey(userId);
        List<CartInfo> list = redisTemplate.opsForHash().values(cartKey);
        List<CartInfo> checkedList = new ArrayList<>();
        list.forEach(cartInfo -> {
            if(cartInfo.getIsChecked()==1){
                checkedList.add(cartInfo);
            }
        });
        return checkedList;
    }
}
